package chatNI;

import java.io.*;
import java.net.*;
import java.util.Observable;
import java.util.Observer;
import messages.*;
import chatControler.ChatControler;
import chatGUI.EcranduChat;
import chatModel.Choix;
import chatModel.Utilisateur;

import chatModel.UtilisateurDistant;
/**
 * Classe définissant l'interface avec le réseau
 * Centralisation des sockets pour l'envoi 
 * et la reception de messages et de fichiers
 * 
 * Elle gère également les notifications faites par l'utilisateur local
 */
public class ChatNI implements Observer {

	/*
	 * Pour pouvoir lancer les 4 Threads différents
	 */
	private DatagramSocket socketUdpEmission;
	private DatagramSocket socketUdpReception;
	private ServerSocket serverTcp;
	private Socket clienttcp;
	private ThreadUdpEcoute ecoute;


	/**
	 * Adresse du broadcast
	 */
	private InetAddress addrBroadcast ;

	/**
	 * Patron MVC
	 */
	private ChatControler chatControler;

        
        /**
         * Constructeur de la classe ChatNI, le ChatControler est obligatoire
         * @param chatControler ChatControler du ChatSystem
         */
	public ChatNI(ChatControler chatControler) {
		super();
		this.chatControler = chatControler;
		chatControler.getLocalUser().addObserver(this);
		this.socketUdpEmission =null;
		this.socketUdpReception =null;
		try {
			this.addrBroadcast = InetAddress.getByName("255.255.255.255");
		} catch (UnknownHostException e) {
			System.out.println("Erreur dans le InetAddress.getByName");
			e.printStackTrace();
		}

		try {
			this.socketUdpEmission = new DatagramSocket();
			this.socketUdpReception = new DatagramSocket(Port.ecouteReceptionMsg);
		} catch (SocketException e) {
			System.out.println("Erreur création de la socket");
			e.printStackTrace();
		}
	}


	/*********************
	 **----METHODES-----*
	 ********************/

        /**
         * Demarre un thread pour envoyer le message
         * @see chatNI.ThreadUdpEnvoie ThreadUdpEnvoie
         * @param myByteToSend Données à envoyer (Message binaire)
         * @param adrDistante Adresse IP du destinataire
         */
	public void sendMessage(byte[] myByteToSend, InetAddress adrDistante){
		ThreadUdpEnvoie threadToSend = new ThreadUdpEnvoie(this.socketUdpEmission, myByteToSend, adrDistante);
		threadToSend.start();
	}

        
        /**
         * Cette méthode sert à envoyer un Hello positif pour se connecter
         */
	public void connect(){
		try {
			System.out.println("envoie du hello");
			byte[] myHelloMsg = MessagesFactory.getHello(chatControler.getLocalUser().getUserName(), true);
			sendMessage(myHelloMsg, this.addrBroadcast);
			// On lance le thread d'écoute et on signifie au controleur qu'on est désormais connecté
			ecoute = new ThreadUdpEcoute(this.socketUdpReception, this.chatControler, true);
			ecoute.start();
		} catch (MessageCreationException e) {
			System.out.println("Probleme dans la création du message");
			e.printStackTrace();
		}
	}
        
        /**
         * Cette méthode sert à envoyer un Bye pour se déconnecter
         */
	public void disconnect(){
		try {
			byte[] myByeMsg = MessagesFactory.getBye(chatControler.getLocalUser().getUserName());
			sendMessage(myByeMsg, this.addrBroadcast);
			ecoute.setActif(false);
			ecoute.interrupt();
		} catch (MessageCreationException e) {
			System.out.println("Probleme dans la création du message");
			e.printStackTrace();
		}

	}
        
        /**
         * Méthode pour recevoir un fichier
         * @see chatNI.ThreadTcpRecup ThreadTcpRecup
         * @param file Fichier conteneur qui sera le fichier qui a été envoyé
         * @param user Utilisateur qui envoi le fichier
         */
	public void rcvFile(File file, UtilisateurDistant user){
		try {
			serverTcp = new ServerSocket(Port.echangeFichierRecup);
			ThreadTcpRecup recupFile = new ThreadTcpRecup(serverTcp, file, this.chatControler, user);
			recupFile.run();
		} catch (IOException e) {
			System.out.println("probleme dans la creation du serverSocket");
			e.printStackTrace();
		}
	}
        
        /**
         * Méthode pour envoyer un fichier
         * @see chatNI.ThreadTcpEnvoie TreadTcpEnvoie
         * @param file Fichier à envoyer
         * @param adrDistante Adresse IP de l'utilisateur distant
         * @param user Utilisateur distant
         */
	public void sendFile(File file, InetAddress adrDistante, UtilisateurDistant user){
		try {
			clienttcp = new Socket(adrDistante, Port.echangeFichier);
			ThreadTcpEnvoie envoieFile = new ThreadTcpEnvoie(clienttcp, file, this.chatControler, user);
			envoieFile.run();
		} catch (IOException e) {
			System.out.println("probleme dans la creation de la socket client");
			e.printStackTrace();
		}
	}
        
        /**
         * Réécriture de la méthode update pour gérer 
         * les notifications de l'utilisateur
         * @param observed L'utilisateur local observé
         * @param object L'utilisateur local
         */
	@Override
	public void update(Observable observed, Object object) {
		if(observed instanceof Utilisateur ){
			Utilisateur tempUtilisateur = (Utilisateur) object;
			
			if(tempUtilisateur.getChoix()==Choix.connexionOk){
				System.out.println("on est dans le NI");
				this.connect();
			}
			
			if(tempUtilisateur.getChoix()==Choix.rcvNewHello){
				this.sendMessage(tempUtilisateur.getMsgToSend(), tempUtilisateur.getUserDistant().getUserDistantIP());

			}
			
			if(tempUtilisateur.getChoix()==Choix.sendMsgOneUser){
				System.out.println("le ni envoie un message pour un");
				byte[] myByteToSend = tempUtilisateur.getMsgToSend();
				sendMessage(myByteToSend, tempUtilisateur.getUserDistant().getUserDistantIP());
			}
			
			if(tempUtilisateur.getChoix()==Choix.sendMsgAllUser){
				System.out.println("le ni envoie un message pour tous !");
				byte[] myByteToSend = tempUtilisateur.getMsgToSend();
				for(int i=0; i<chatControler.getOtherUserList().size();i++){
					if(i>0){//pour éviter qu'il envoie en broadcast
						sendMessage(myByteToSend, ((UtilisateurDistant)(chatControler.getOtherUserList().get(i))).getUserDistantIP());
					}
				}
			}
			
			if(tempUtilisateur.getChoix()==Choix.sendFileNotifAllUser){
				byte[] myByteToSend = tempUtilisateur.getMsgToSend();
				for(int i=0; i<chatControler.getOtherUserList().size();i++){
					if(i>0){//pour éviter qu'il envoie en broadcast
						sendMessage(myByteToSend, ((UtilisateurDistant)(chatControler.getOtherUserList().get(i))).getUserDistantIP());
					}
				}
			}
			
			if(tempUtilisateur.getChoix()==Choix.sendFileNotifOneUser){
				byte[] myByteToSend = tempUtilisateur.getMsgToSend();
				sendMessage(myByteToSend, tempUtilisateur.getUserDistant().getUserDistantIP());
			}
			
			if(tempUtilisateur.getChoix()==Choix.confAcceptFileOk){
				byte[] myByteToSend = tempUtilisateur.getMsgToSend();
				sendMessage(myByteToSend, tempUtilisateur.getUserDistant().getUserDistantIP());
				rcvFile(tempUtilisateur.getFile(), tempUtilisateur.getUserDistant());
			}
			
			if(tempUtilisateur.getChoix()==Choix.confAcceptFileNoOk){
				byte[] myByteToSend = tempUtilisateur.getMsgToSend();
				sendMessage(myByteToSend, tempUtilisateur.getUserDistant().getUserDistantIP());
			}
			
			if(tempUtilisateur.getChoix()==Choix.rcvAcceptedFile){
				sendFile(tempUtilisateur.getFile(),tempUtilisateur.getUserDistant().getUserDistantIP(), tempUtilisateur.getUserDistant());
			}
			
			if(tempUtilisateur.getChoix()==Choix.deconnexionOk){
				System.out.println("le NI se deconnecte");
				this.disconnect();
			}

			if(tempUtilisateur.getChoix()==Choix.quitChat){
				System.out.println("NI quitte");
				this.disconnect();
			}
		}

	}

}
